package com.nx.arch.addon.lock.lock;

import java.net.SocketTimeoutException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.nx.arch.addon.lock.client.EtcdClient;
import com.nx.arch.addon.lock.client.EtcdResponse;
import com.nx.arch.addon.lock.exception.LockException;
import com.nx.arch.addon.lock.util.RenewTask;
import com.nx.arch.addon.lock.util.RandomUtil;

/**
 * @类名称 EtcdLock.java
 * @类描述 etcd锁对象：
 * <pre>
 * casExist : httpcode 421 - result errorcode 105 key has exsit
 * - resultcode 101 compare error
 * httpcode 201 created
 * </pre>
 * @作者  庄梦蝶殇 linhuaichuan@naixuejiaoyu.com
 * @创建时间 2020年3月28日 下午3:42:16
 * @版本 1.0.0
 *
 * @修改记录
 * <pre>
 *     版本                       修改人 		修改日期 		 修改内容描述
 *     ----------------------------------------------
 *     1.0.0 		庄梦蝶殇 	2020年3月28日             
 *     ----------------------------------------------
 * </pre>
 */
public class EtcdLock extends BaseLock {
    private static Logger logger = LoggerFactory.getLogger("etcd-lock");
    
    protected EtcdClient client;
    
    /**
     * 续租线程
     */
    private RenewTask hbThread = null;
    
    EtcdLock(EtcdClient client, String clusterName, String key, boolean reentrant) {
        super(clusterName + "_" + key, "EtcdLock_Name_" + RandomUtil.nextInt(), reentrant);
        this.client = client;
    }
    
    @Override
    protected boolean lock()
        throws LockException {
        try {
            // 抢锁
            EtcdResponse etcdResult = client.casExist(key, value, "false", this.ttl);
            if ((etcdResult != null && !etcdResult.isError()) && (etcdResult.httpCode == 200 || etcdResult.httpCode == 201)) {
                // 上报监控
                // NxMonitor.sum(30, 1);
                hbThread = new RenewTask(client, key, value, this.ttl);
                hbThread.setDaemon(true);
                hbThread.start();
                hold.set(true);
            } else {
                hold.set(false);
                if (etcdResult != null && etcdResult.isError() && (etcdResult.errorCode > 200)) {
                    throw new LockException("etcd cluster Related Error or param error : code" + etcdResult.errorCode, etcdResult);
                }
            }
        } catch (LockException e) {
            hold.set(false);
            Throwable cause = e.getCause();
            if (cause instanceof SocketTimeoutException) {
                return hold.get();
            }
            logger.error("Error encountered when attempting to acquire lock", e);
            throw e;
        } finally {
            if (reentrant && hold.get()) {
                holdLocks.get().add(key);
            }
        }
        return hold.get();
    }
    
    @Override
    public void release()
        throws LockException {
        if (hold.get()) {
            try {
                hold.set(false);
                holdLocks.get().remove(key);
                client.casDelete(key, value);
            } catch (LockException e) {
            } finally {
                if (hbThread != null) {
                    hbThread.close();
                }
            }
        }
    }
    
    @Override
    protected Object getClient() {
        return client;
    }
    
    public String getLockValue() {
        return value;
    }
}
